package com.onepiece.shipelves.common.utils;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

/**
 * @author wangxj
 * @Desc: 最小二乘数算法
 * @date 2019-11-28
 */
public class LineRegressionAlg {
    /**
     * 最小二乘法
     *
     * @param X
     * @param Y
     * @return y = ax + b
     */
    public static Map<String, Double> lineRegression(Double[] X, Double[] Y) {
        if (null == X || null == Y || 0 == Y.length || X.length != Y.length) {
            throw new RuntimeException();
        }

        // x平方差和
        double sxx = varianceSum(X);
        // y平方差和
//        double syy = varianceSum(Y);
        // xy协方差和
        double sxy = covarianceSum(X, Y);

        double xAvg = arraySum(X) / X.length;
        double yAvg = arraySum(Y) / Y.length;

        double a = sxy / sxx;
        double b = yAvg - a * xAvg;

        // 相关系数
//        double r = sxy / Math.sqrt(sxx * syy);
        HashMap<String, Double> result = new HashMap<>();
        result.put("a", a);
        result.put("b", b);
//        result.put("r", r);

        return result;
    }

    /**
     * 计算方差和
     *
     * @param X
     * @return
     */
    private static double varianceSum(Double[] X) {
        double xAvg = arraySum(X) / X.length;
        return arraySqSum(arrayMinus(X, xAvg));
    }

    /**
     * 计算协方差和
     *
     * @param X
     * @param Y
     * @return
     */
    private static Double covarianceSum(Double[] X, Double[] Y) {
        double xAvg = arraySum(X) / X.length;
        double yAvg = arraySum(Y) / Y.length;
        return arrayMulSum(arrayMinus(X, xAvg), arrayMinus(Y, yAvg));
    }

    /**
     * 数组减常数
     *
     * @param X
     * @param x
     * @return
     */
    private static Double[] arrayMinus(Double[] X, Double x) {
        int n = X.length;
        Double[] result = new Double[n];
        for (int i = 0; i < n; i++) {
            result[i] = X[i] - x;
        }

        return result;
    }

    /**
     * 数组求和
     *
     * @param X
     * @return
     */
    private static double arraySum(Double[] X) {
        double s = 0;
        for (double x : X) {
            s = s + x;
        }
        return s;
    }

    /**
     * 数组平方求和
     *
     * @param X
     * @return
     */
    private static double arraySqSum(Double[] X) {
        double s = 0;
        for (double x : X) {
            s = s + Math.pow(x, 2);
            ;
        }
        return s;
    }

    /**
     * 数组对应元素相乘求和
     *
     * @param X
     * @return
     */
    private static Double arrayMulSum(Double[] X, Double[] Y) {
        double s = 0;
        for (int i = 0; i < X.length; i++) {
            s = s + X[i] * Y[i];
        }
        return s;
    }

    public static void main(String[] args) {
        Random random = new Random();
        Double[] X = new Double[20];
        Double[] Y = new Double[20];

        for (int i = 1; i <= 20; i++) {
            X[i - 1] = Double.valueOf(i);
            Y[i - 1] = Double.valueOf(i - 1);
        }

        System.out.println(lineRegression(X, Y));
    }
}
